Pro Entity Framework Core 2 for ASP.NET Core MVC 翻译

第 3 章 使用数据库

作者:Adam Freeman
翻译:陈广
日期:2018-12-2


Entity Framework Core 使用数据库的使用变得容易,但是,这并不能使开发人员了解它们是如何工作的,以及应用程序执行的操作是如何转换为 SQL 命令的,特别是在出现意外结果时。本章我将向您展示 Visual Studio 为使用数据库提供的工具,并向您展示如何执行不同类型的 SQL 命令。使用 Entity Framework Core 并不需要成为 SQL 方面的专家,但是如果没有得到预期的结果,了解基本知识可能会有所帮助。


选择数据库服务器和提供程序包

在选择数据库服务器时很难出错,因为所有可用的选项都是好产品。不管您是喜欢商业产品还是想要开源产品,还是想运行自己的服务器或使用云,都无关紧要。

如果您已经有了数据库服务器,可能是因为公司的采购标准或网站许可协议,所以选择了它。Entity Framework Core 消除了数据库服务器之间的差异,使用哪一个数据库并不重要。当然,除非您有非常特殊的要求,否则不值得与您公司的技术标准作斗争。

如果您还没有数据库服务器,那么就有很多好的选择。Microsoft SQL Server 是我最常使用的数据库服务器,因为它有各种各样的定价方案,包括开发人员和小型项目的零成本选项,以及在 Azure 上可用的托管版本。在本书中,我使用了 SQL Server 的零配置开发人员版本(称为 LocalDB)作为示例。

(我没有因推荐 SQL Server 或我在书中使用或提到的任何产品而获得任何类型的奖励。使用我指定的软件来编写示例是很重要的,但这并不限制您在实际项目中的选择。)

如果您更喜欢开源软件,那么 MySQL 是一个很好的选择,尽管它是由 Oracle 管理的,具有混合的开源跟踪记录。MariaDB 是 MySQL 项目的一个分支,它不涉及 Oracle,但目标是维护兼容性。有许多提供商提供 MySQL 或其衍生产品作为托管/云服务,包括 Amazon Web 服务和 Microsoft Azure。

一旦选择了数据库服务器,就可以选择与 Entity Framework Core 一起使用的数据库提供程序。Microsoft 为最流行的数据库维护一个提供程序列表:https://docs.microsoft.com/en-us/ef/core/providers。大多数提供程序包都是免费使用的,但也有一些商业产品可用。如果您想使用 Oracle 数据库服务器(商业产品,而不是 MySQL),那么您需要第三方提供商授权,因为 Oracle 还没有生成自己的包。


为本章作准备

本章我将继续使用在第2章创建的 PartyInvites。为为本章作准备,请打开一个新的 PowerShell 窗口或命令提示符,导航到 PartyInvites 项目文件夹(其中包含 libman.json 文件),并运行清单 3-1 所示的命令。这些命令删除和重建应用程序所使用的数据库,这将有助于确保您获得本章示例的预期结果。

清单 3-1:重新设置示例应用程序数据库

dotnet ef database drop --force
dotnet ef database update

将清单3-2中的配置语句添加至 appsettings.json 文件中。这些语句禁用除 Entity Framework Core 外的所有 .NET 程序包的日志消息,它将使编写示例变得更为容易。

清单 3-2:PartyInvites 文件夹下的 appsettings.json 文件,配置日志消息

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=PartyInvites"
  },
  "Logging": {
    "LogLevel": {
      "Default": "None",
      "Microsoft.EntityFrameworkCore": "Information"
    }
  }
}

此日志记录配置将使您看到 Entity Framework Core 产生的消息,这些消息显示发送到数据库的 SQL 命令,并防止它们在其他消息流中丢失。

通过在项目文件夹中执行dotnet run命令启动应用程序,并使用浏览器窗口导航到 http://localhost:5000。单击【RSVP Now】按钮,使用表3-1所示的值创建四个 RSVP 回复。

表 3-1:示例应用程序所需的数据值

Name Email Phone WillAttend
Alice Jones alice@example.com 555-123-5678 Yes
Peter Davies peter@example.com 555-456-7890 Yes
Dora Francis dora@example.com 555-456-1234 Yes
Bob Smith bob@example.com 555-123-1234 No

当添加回复完成后,导致至 http://localhost:5000/home/listresponses,您将看到如图3-1所示的列表。

图3-1 示例应用程序添加数据

浏览数据库

Visual Studio 包括一组工具用于浏览使用过的数据库。从 Visual Studio【工具】菜单中选择【连接到数据库】,您将看到如图3-2所示的对话框,它允许您选择将要连接的数据库服务器的类型。

图3-2 选择数据库服务器类型

本书的所有示例都是使用 LocalDB。LocalDB 是 Microsoft SQL Server 的一个版本,在第2章作为 Visual Studio 工作负载的一部分安装,并被设计为供开发者使用。它实现了 SQL Server 的所有关键功能,但无需进行配置,也不需要生产系统的许可。在 Visual Studio 提供的选项列表中选择【Microsoft SQL Server】,并单击【继续】按钮。

下一个对话框允许您指定要使用的数据库服务器和数据库的详细信息,如图3-3所示。在【服务器名】中输入(localdb)\MSSQLLocalDB,并在【选择或输入数据库名称】下拉列表中选择PartyInvites,如图3-3所示。

图3-3 选择数据库服务器及数据库

提示:连接到 LocalDB 所需的字符串可能导致混淆。第一部分是由括号包围的localdb,然后是单个\字符,后面跟着MS_SQL_LocalDB,但没有下划线:(localdb)\MSSQLLocalDB。在 appsetting.json 文件中指定连接字符串时需要额外的\字符,因为反斜杠具有特殊意义,必须转义:(localdb)\\MSSQLLocalDB

单击【确定】按钮,Visual Studio 将连接至数据库服务器并在【服务器资源管理器】窗口中显示详细信息(如果窗口没有打开,选择【视图】➤【服务器资源管理器】)。在【数据连接】部分,您将看到 PartyInvites 条目,展开后可看到数据库的详细信息,如图3-4所示。

图3-4 服务器资源管理器窗口中的数据库条目

检查数据表

如果您展开【表】项,将看到两个条目。【__EFMigrationsHistory】表用于跟踪应用于数据库的迁移,以使其与应用程序的数据模型保持同步。我在第13章详细解释了迁移是如何工作的,本章对此表不感兴趣。【Responses】表用于存储应用程序的GuestResponse对象。在【服务器资源管理器】中展开此表,您可以看到表列,如图3-5所示。

图3-5 数据库中的 Responses 表的结构

您可以看到 Entity Framework Core 如何使用应用程序的详细信息来创建存储GuestResponse对象的表。表的名称取自数据库 context 类中的属性,列的名称对应于GuestResponse类定义的属性。这些是 Entity Framework Core 默认遵循的约定,我将在第2部分中解释如何覆盖它们。

.NET 使用的数据类型必须转换为数据库服务器支持的数据类型。Entity Framework Core 确定在创建数据库时应该使用哪些数据库类型,您可以通过右键单击【服务器资源管理器】窗口中的【Responses】项并从弹出菜单中选择打开表定义来检查其决策。Visual Studio 将打开一个新的编辑器窗口,其中包含表结构的详细信息,如图3-6所示。

图3-6 浏览数据库表的结构

您可以看到已选择用于表示每个GuestResponse属性值的 SQL 数据类型。Entity Framework Core 依赖于数据库提供程序来选择最佳 SQL 数据类型,对于不同的数据库服务器,所选择的类型也可能有所不同。对于 SQL Server,您可以看到Id属性被存储为 SQL bigint,对应于 .NET 的 longWillAttend属性将被存储为bit,对应于 .NET 的可空bool;其它属性将被存储为nvarchar(MAX),对应于 .NET 的string

提示:对于大多数项目,Entity Framework Core 选择的数据类型将是完全可以接受的,但是您可以使用第21章中描述的特性来指定不同的类型。

检查数据库内容

在【服务器资源管理器】窗口中的表上点击鼠标右键,在弹出菜单中选择【显示表数据】,以查看表中的数据。Visual Studio 将查询数据库并显示结果,如图3-7所示。

图3-7 显示数据表中的数据

Visual Studio 用于显示数据的网格是可编辑的,这意味着您可以更新已存在的数据,并在不使用 SQL 语句的情况下直接添加新行。

使用表3-2所示的值添加一个新的回复,以填充网格底部行中的字段,然后按 Tab 键。您将无法输入Id列的值,但是当数据添加到数据库时,数据库服务器将为您分配一个值。

表 3-2:将一行添加到数据表的数据值

Email Name Phone WillAttend
jane@example.com Jane Marshall 555-123-1212 True

当您按下 Tab 按钮,Visual Studio 将向数据库添加新的数据。您可以通过在应用程序中导航到 http://localhost:5000/home/listresponses 来查看新数据,如图3-8所示。

图3-8 应用程序显示的新数据

理解 SQL

了解 Entity Framework Core 如何将应用程序的操作转换为数据库服务器的命令通常是有用的。使用 Entity Framework Core 并不需要是 SQL 方面的专家,但是了解基本知识可以帮助您判断什么时候没有得到预期的结果。在接下来的部分中,我将描述基本的 SQL 命令,并解释 Entity Framework Core 是如何使用这些命令的。

查询数据

SQL 的基本功能是查询,它从数据库中检索数据。如果您导航至 http://localhost:5000/home/listresponses,并检查由应用程序生成的日志消息,将会看到 Entity Framework Core 从数据库获取数据所使用的 SQL 命令,如下:

SELECT [r].[Id], [r].[Email], [r].[Name], [r].[Phone], [r].[WillAttend]
FROM [Responses] AS [r]
ORDER BY [r].[WillAttend] DESC

如果您熟悉 LINQ,那么就可以通过阅读这个 SQL 命令来了解它所做的事情,但是有必要进一步探讨一下,以了解 Entity Framework Core 在做什么。

手动查询数据库

您可以在 Visual Studio 的【服务器资源管理器】窗口中,通过右键单击【表】并从弹出菜单中选择【新建查询】来直接查询数据库。在使用 SQL Server 时,我更喜欢通过从【工具】➤【SQL Server】菜单中选择【New Query】来使用不同的 Visual Studio 功能。当您看到如图3-9所示的【连接】对话框时,在【服务器名称】中输入 (localdb)\MSSQLLocalDB,保留【数据库名称】为<默认值>,并单击【连接】按钮。

图3-9 连接至数据库

提示:连接到数据库后,连接设置将保存在【连接】对话框窗口的 History 表中,以后可以用于连接,而不必重新输入详细信息。

单击【连接】按钮,Visual Studio 将连接至数据库服务器,并打开一个新的编辑面板,你可以在其中输入 SQL 命令。在新面板中输入如清单3-3所示的命令。

清单 3-3:一个基本的 SQL 查询

USE PartyInvites
SELECT * FROM Responses

单击编辑器窗格左上角的绿色箭头,或右击窗口,在弹出菜单中选择【Execute】以执行命令。Visual Studio 将发送 SQL 命令至数据库服务器并显示结果,如图3-10所示。

图3-10 查询数据库

由于这是您直接执行的第一个 SQL 命令,我将分析其中的每个部分,并解释它的含义。以下是第一部分:

USE PartyInvites

此命令选择了 PartyInvites 数据库。一个数据库服务器可以管理多个数据库,选择您想要使用的数据库非常重要(您可以在创建连接或使用查询窗口顶部的下拉列表时选择数据库,但我更喜欢显式地进行选择)。

清单3-3中 SQL 语句的第二部分是实际的查询语句,如下:

SELECT * FROM Responses

SELECT关键字表示 SQL 查询。FROM关键字指定所需数据的表。星号表示查询将返回表中所有列的值。综合起来,这个查询告诉数据库服务器:“将 Responses 表中的所有列都给我”。此查询产生表3-3所示的结果。

表 3-3:基础查询的结果

Id Name Email Phone WillAttend
1 Alice Jones alice@example.com 555-123-5678 1
2 Peter peter@example.com 555-456-7890 1
3 Dora Francis dora@example.com 555-456-1234 1
4 Bob Smith bob@example.com 555-123-1234 0
5 Jane Marshall jane@example.com 555-123-1212 1

过滤数据

默认情况下,查询将返回表中的所有行。对于存储少量数据(例如示例项目)的应用程序来说,这是完全合理的,但是在大多数实际项目中,您需要查询数据的子集。WHERE关键字用于从数据表中选择特定行。将清单3-4中所示的 SQL 输入到 Visual Studio 查询窗口,以查看这是如何工作的。


结构化查询语言约定

您将注意到,本章中的 SQL 关键字使用大写字符。这不是必需的,但这样做是常见的做法,它有助于使复杂的 SQL 更容易阅读。SQL 的世界充满约定和首选项 —— 和 C# 开发一样 —— 您将不可避免地遇到坚持某种风格的数据库管理员和开发人员。数据库服务器擅长解析 SQL,我的建议是采用您和您的团队易于阅读的样式,即使这意味着编写倾向于 C# 编码约定的命令,而不是那些与 SQL 关联的命令。


清单 3-4:查询选定的行

USE PartyInvites
SELECT * FROM Responses
WHERE WillAttend = 1

WHERE关键字后面跟着一个表达式,该表达式将匹配结果中应该包含的行。本例中,我已经指定了将WillAttend值为 1 的行应该包含在结果中,而其他行应该被排除在外。执行此命令,您将只看到匹配的响应,如表3-4所示。

表 3-3:使用 WHERE 关键字的结果

Id Name Email Phone WillAttend
1 Alice Jones alice@example.com 555-123-5678 1
2 Peter peter@example.com 555-456-7890 1
3 Dora Francis dora@example.com 555-456-1234 1
4 Jane Marshall jane@example.com 555-123-1212 1

选择和排序列

查询中的星号(*字符)询问结果中包含的行的所有列。您并不总是需要所有的列,您可能需要指定结果中列的显示顺序。SQL 查询可以对结果中包含的列进行选择,如清单3-5所示。

清单 3-5:选择和排序列

USE PartyInvites
SELECT Id, Name, Email FROM Responses
WHERE WillAttend = 'true'

在此查询中,我指定了只需要IdNameEmail列值。当执行查询,您将看到表3-5所示的结果,其他列的值已被排除。注意,结果是按照我指定列的顺序显示的。

表 3-5:选择列的结果

Id Name Email
1 Alice Jones alice@example.com
2 Peter peter@example.com
3 Dora Francis dora@example.com
5 Jane Marshall jane@example.com

排序行

可以使用ORDER BY关键字指定结果中返回匹配行的顺序。在清单3-6中,我使用ORDER BY告诉数据库服务器,我希望通过Email列的值来排序结果。

清单 3-6:排序结果

USE PartyInvites
SELECT Id, Name, Email FROM Responses
WHERE WillAttend = 'true'
ORDER BY Email

当您执行查询,结果将按Email列的值进行排序,如表3-6所示。

表 3-6:排序行的结果

Id Name Email
1 Alice Jones alice@example.com
3 Dora Francis dora@example.com
5 Jane Marshall jane@example.com
2 Peter peter@example.com

检查 Entity Framework Core 查询

SQL查询功能很多,但前面几节中显示的特性足以了解从数据库中请求的数据。Entity Framework Core 产生的查询有一些细微的差异值得解释。在清单3-7中,我已经更改了 Home 控制器的ListResponses action 方法中使用的 LINQ 查询,以便对它请求的数据进行更多的选择。

清单 3-7:Controllers 文件夹下的 HomeController.cs 文件,更改 LINQ 查询

using Microsoft.AspNetCore.Mvc;
using PartyInvites.Models;
using System.Linq;

namespace PartyInvites.Controllers
{
    public class HomeController : Controller
    {
        private DataContext context;

        public HomeController(DataContext ctx) => context = ctx;

        public IActionResult Index() => View();

        public IActionResult Respond() => View();

        [HttpPost]
        public IActionResult Respond(GuestResponse response)
        {
            context.Responses.Add(response);
            context.SaveChanges();
            return RedirectToAction(nameof(Thanks),
                new { Name = response.Name, WillAttend = response.WillAttend });
        }

        public IActionResult Thanks(GuestResponse response) => View(response);

        public IActionResult ListResponses() =>
            View(context.Responses
                .Where(r => r.WillAttend == true)
                .OrderBy(r => r.Email));
    }
}

在项目文件夹下使用dotnet run启动应用程序,导致航至 http://localhost:5000/home/listresponses。显示给用户的 HTML 将只包含与会者,按他们的电子邮件地址排序,如图3-11所示。

图3-11 在示例应用程序中使用更具选择性的查询

如果您检查应用程序生成的日志消息,将看到 Entity Framework Core 从 LINQ 查询生成的 SQL。

SELECT [r].[Id], [r].[Email], [r].[Name], [r].[Phone], [r].[WillAttend]
FROM [Responses] AS [r]
WHERE [r].[WillAttend] = 1
ORDER BY [r].[Email]

您可以看到,查询的结构遵循与我在前几节中构建的相同的模式。使用方括号([]字符)允许列名包含特殊字符,例如空格。对于示例应用程序中的数据模型来说,这不是必需的,但是 Entity Framework Core 还是会这样做。

AS关键字用于创建临时别名。Entity Framework Core 使用AS关键字来引用别名为r的 Responses 表。这在简单的查询中没有好处,但对于组合多个表的数据的更复杂的查询来说,是有帮助的。

理解查询参数

在检查 Entity Framework Core 创建的查询时,您可能会看到另一个不同之处。为了演示,我更改了ListResponses方法使用的查询,如清单3-8所示。

清单 3-8:Controllers 文件夹下的 HomeController.cs 文件,更改 LINQ 查询

using Microsoft.AspNetCore.Mvc;
using PartyInvites.Models;
using System.Linq;

namespace PartyInvites.Controllers
{
    public class HomeController : Controller
    {
        private DataContext context;

        public HomeController(DataContext ctx) => context = ctx;

        public IActionResult Index() => View();

        public IActionResult Respond() => View();

        [HttpPost]
        public IActionResult Respond(GuestResponse response)
        {
            context.Responses.Add(response);
            context.SaveChanges();
            return RedirectToAction(nameof(Thanks),
                new { Name = response.Name, WillAttend = response.WillAttend });
        }

        public IActionResult Thanks(GuestResponse response) => View(response);

        public IActionResult ListResponses(string searchTerm = "555-123-5678") =>
            View(context.Responses
                .Where(r => r.Phone == searchTerm)
                .OrderBy(r => r.Email));
    }
}

LINQ 查询使用WHERE方法选择其Phone值与搜索参数匹配的GuestResponse对象。如果启动应用程序,导航到 http://localhost:5000/home/listresponses,并检查应用程序生成的日志消息,您将看到如何将其转换为 SQL。

SELECT [r].[Id], [r].[Email], [r].[Name], [r].[Phone], [r].[WillAttend]
FROM [Responses] AS [r]
WHERE [r].[Phone] = @__searchTerm_0
ORDER BY [r].[Email]

@字符用于表示 SQL 查询中的参数。当 Entity Framework Core 处理变量时使用参数,它们允许数据库服务器识别类似的查询,并通过同样的方式处理它们,从而提高性能。参数还提供了防止 SQL 注入攻击的保护,防止用户输入的数据值可以解释为 SQL 命令的一部分。

使用参数是个好主意 —— 特别是在 Entity Framework Core 不知道数据值的源是否可以信任的情况下 —— 但这确实使您更难理解发送到数据库的是什么 SQL ,因为默认情况下参数值不会显示在日志记录消息中。大多数情况下,这不是一个问题,因为有兴趣的是查询的结构,但是在清单3-9中,我更改了示例应用程序的配置,以便 Entity Framework Core 将在它生成的日志消息中包含参数值。

警告:不要在生产中启用此选项,因为敏感数据最终将出现在应用程序的日志文件中。

清单 3-9:PartyInvites 文件夹下的 Startup.cs 文件,启用参数值日志记录

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using PartyInvites.Models;

namespace PartyInvites
{
    public class Startup
    {
        public Startup(IConfiguration config) => Configuration = config;

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            string conString = Configuration["ConnectionStrings:DefaultConnection"];
            services.AddDbContext<DataContext>(options => {
                options.EnableSensitiveDataLogging(true);
                options.UseSqlServer(conString);
            });
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseDeveloperExceptionPage();
            app.UseStatusCodePages();
            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }
    }
}

使用EnableSensitiveDataLogging方法告诉 Entity Framework Core 在日志消息中包含参数值。使用dotnet run重启应用程序并导航至 http://localhost:5000/home/listresponses,在应用程序生成的日志记录消息中,Entity Framework Core 将在查询之前显示的消息中包含参数值。

Executed DbCommand (15ms) [Parameters=[@__searchTerm_0='555-123-5678' (Size = 4000)], CommandType='Text',   CommandTimeout='30']
SELECT [r].[Id], [r].[Email], [r].[Name], [r].[Phone], [r].[WillAttend]
FROM [Responses] AS [r]
WHERE [r].[Phone] = @__searchTerm_0
ORDER BY [r].[Email]

存储和更新数据

在大多数应用程序中,查询是最常见的命令类型,但存储新数据和更新已存数据也非常重要。INSERT命令用于将新的数据行插入到表中,清单3-10所示的 SQL 命令向 Responses 表添加一个新行。

清单 3-10:向表中插入数据

USE PartyInvites
INSERT INTO Responses(Name, Email, Phone, WillAttend)
VALUES ('Joe Dobbs', 'joe@example.com', '555-888-1234', 1)

INSERT INTO关键字后面是要为其提供值的列的集合。然后使用VALUES关键字,然后是要存储在数据库中的值,它与列表相同的顺序表示。清单中的命令添加了一个新行,其中包含NameEmailPhoneWillAttend列的值。Id列不需要值,它由 Entity Framework Core 进行配置,以便数据库服务器在存储其余列时生成值。

提示:请注意,SQL 中的字符串是用的单引号('字符)而不是 C# 所使用的双引号("字符)来标记的。

当您执行清单3-10的命令,将看到以下信息:

(1 row(s) affected)

数据库服务器通过指示更改了多少行来响应修改数据库的命令。要查看 Entity Framework Core 使用的INSERT命令,请导航到 http://localhost:5000,单击【RSVP Now】按钮,然后使用表3-7中的值创建响应。

表 3-7:创建回复的值

Name Email Phone WillAttend
Anna Roth anna@example.com 555-204-7692 False

如果检查应用程序在数据库中存储新数据时生成的日志记录消息,您将看到使用了相同的基本类型的INSERT命令,但有一些不同之处。

Executed DbCommand (3ms) [Parameters=[@p0='anna@example.com' (Size = 4000),
    @p1='Anna Roth' (Size = 4000), @p2='555-204-7692' (Size = 4000),
    @p3=False' (Nullable = true)], CommandType='Text', CommandTimeout='30']

SET NOCOUNT ON;

INSERT INTO [Responses] ([Email], [Name], [Phone], [WillAttend])
VALUES (@p0, @p1, @p2, @p3);

SET NOCOUNT ON命令禁用报告受命令影响的行数。它对本章中的简单命令几乎没有影响,但对于更复杂的操作,可以提高性能。您可以看到,Entity Framework Core 使用的INSERT命令类似于清单3-10中的命令,但具有参数化的值。

INSERT之后,Entity Framework Core 立即向数据库服务器发送另一个命令。

SELECT [Id]
FROM [Responses]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();

Entity Framework Core 使用此命令检查受INSERT命令影响的行数,并确定数据库服务器分配给Id列的值唯一值。了解Id值对于后续的新存储数据所执行的操作非常重要,Entity Framework Core 执行此查询,尽管示例应用程序中没有这样的操作。

更新存在的数据

UPDATE命令用于更改之前存储于数据库的数据,它可以在一个操作中更改多个行。清单3-11所示的命令更改 Responses 表中WillAttend值为 1 的行,并更改它们的电话号码。(这本身并不是一个特别有用的更改,但它确实演示了UPDATE的基本结构。)

清单 3-11:更新已存数据

USE PartyInvites

UPDATE Responses
SET Phone='404-204-1234'
WHERE WillAttend = 1

UPDATE关键字后面跟着表名,SET关键字用于指定被改变的列和值。WHERE关键后面的表达式选择被更改的列。当您执行此命令,数据库服务器将响应被影响的行数。

(5 row(s) affected)

执行清单3-12所示的命令以查询所有 Responses 表中的行。

清单 3-12:查询所有行

USE PartyInvites
SELECT * FROM Responses

表3-8显示了本次查询的结果,并高亮了执行清单3-11更新所影响的数据。

表 3-8:更新已存数据的效果

Id Name Email Phone WillAttend
1 Alice Jones alice@example.com 404-204-1234 1
2 Peter peter@example.com 404-204-1234 1
3 Dora Francis dora@example.com 404-204-1234 1
4 Bob Smith bob@example.com 555-123-1234 0
5 Jane Marshall jane@example.com 404-204-1234 1
6 Joe Dobbs joe@example.com 404-204-1234 1
7 Anna Roth anna@example.com 555-204-7692 0

删除数据

最后一个命令是从数据库中删除数据的命令:DELETE命令。必须谨慎使用此命令,因为它有一个WHERE子句,该子句允许单个命令选择多个行进行删除,而且很容易意外地从数据库中删除比预期更多的数据。清单3-13中所示的命令删除了 Responses 表中的所有WillAttend值为零的行。

清单 3-13:删除数据

USE PartyInvites
DELETE FROM Responses
WHERE WillAttend = 0

执行此命令,然后重复清单3-12中的查询,以确认已从数据库中删除已拒绝的邀请。示例应用程序中并没有对删除数据的支持,但是您将在后面的章节中看到这种类型的命令,包括作为我在下一章开始构建的 SportsStore 应用程序的一部分。

连接数据

数据库可以包含表之间的关系,Entity Framework Core 用来跟踪对象之间的关系。此特性构建在本章前面描述的INSERTUPDATE命令上,但依赖于一种更复杂的查询类型,称为连接,它合并了来自多个表的数据。连接可能会令人困惑;您不需要理解它们才能使用 Entity Framework Core,但是拥有基本知识可能是有用的。

注意:不用担心你没立即跟上这一段。一旦阅读了第14章,它将更有意义,其中包括使用连接的 Entity Framework Core 特性,并在第15章和第16章中对其进行了深入描述。

准备数据库

为演示连接是如何工作的,我需要在数据库中添加另一张表。执行 清单3-14所示的使命令,它将创建一个名为 Preferences 的表。

清单 3-14:在数据库中创建一个新表

USE PartyInvites

DROP TABLE IF EXISTS Preferences

CREATE TABLE Preferences (
	Id bigint IDENTITY,
	Email nvarchar(max),
	NutAllergy bit,
	Teetotal bit,
	ResponseId bigint,
)

DROP TABLE命令告诉数据库服务器删除 Preferences 表(如果它已经存在),这意味着您可以重复执行清单3-14中所示的命令,而不会导致错误。

CREATE TABLE命令用于创建一个新表。它指定了表的名称,以及表将包含的列,以及每个列的类型。清单3-14中的命令所创建的表有IdEmailNutAllergyTeetotal列。Id列配置了IDENTITY关键字,这使得数据库服务器负责创建唯一值,以及默认情况下哪个 Entity Framework Core 应用于主键列。清单3-15中所示的命令将新行插入到 Responses 和 Preferences 表中。

清单 3-15:排序相关数据

USE PartyInvites

INSERT INTO Responses(Name, Email, Phone, WillAttend)
VALUES ('Dave Habbs', 'dave@example.com', '555-777-1234', 1)

INSERT INTO Preferences (Email, NutAllergy, Teetotal)
VALUES ('dave@example.com', 0, 1)

第一个INSERT命令将一行添加到 Responses 表中。第二个INSERT命令将一行添加到Preferences表中。执行清单3-16所示的命令来查询这些表并查看新的数据。

清单 3-16:查询新数据

USE PartyInvites
SELECT * FROM Responses
SELECT * FROM Preferences

第一个SELECT命令查询数据库中的所有 Response 行,并包含表3-9中所示的行,该行由清单3-15中的第一个INSERT命令创建。

表 3-9:新的 Responses 行

Id Name Email Phone WillAttend
8 Dave Habbs dave@example.com 555-777-1234 1

第二个SELECT命令查询数据库获取所有的 Preferences 行,并产生如表3-10所示的结果。注意 Preferences 表中行的ResponseId属性与表3-9中所示行的Id值匹配。

表 3-10:新 Preferences 行

Id Email NutAllergy Teetotal
1 dave@example.com 0 555-777-1234 1

执行一个连接

有不同类型的连接,但 Entity Framework Core 使用的类型称为内连接(inner join),它从共享公共值的表中选择行。清单3-17包含一个查询,该查询对 Responses 和 Preferences 执行内连接。

清单 3-17:执行一个内连接

USE PartyInvites

SELECT Responses.Email, Responses.Name, Preferences.NutAllergy, Preferences.Teetotal
FROM Responses
INNER JOIN Preferences ON Responses.Email = Preferences.Email

SELECT命令向数据库服务器请求两个表中的列,INNER JOIN关键字用于指定 Responses 表中的行应该与 Preferences 表中的行连接,其中两者具有相同的 Email 值。结果是,清单3-16中创建的两行的数据值被组合成一个结果,如表3-11所示。

表 3-11:JOIN 的结果

Email Name NutAllergy Teetotal
dave@example.com Dave Habbs 0 1

这是一个简单的连接,但它展示了基本机制,并将帮助您理解 Entity Framework Core 用于从存储复杂数据模型的数据库中获取数据的查询。

总结

在本章中,我向您展示了如何使用 Visual Studio 检查用于存储应用程序数据的数据库。我还向您展示了如何执行基本 SQL 命令,以便您了解 Entity Framework Core 是如何使用数据库的,并且可以检查您是否获得了预期的结果。在下一章中,我将开始创建一个更复杂和更现实的应用程序的过程:SportsStore。

;

© 2018 - IOT小分队文章发布系统 v0.3